package com.ss.android.auto.launch.tasks.collector

import android.util.Log
import androidx.annotation.MainThread
import com.bytedance.news.common.service.manager.BuildConfig
import com.ss.android.auto.utils.HandlerUtils
import com.ss.android.helper.DebugChannelHelper

/**
 *  @author zhongxinyuan @ ByteDance Inc
 *  @since   2023/2/1
 */
const val TAG = "TaskCollector"
abstract class TaskCollector {
    private val subTasks: MutableList<SubTask> = mutableListOf()
    private var groupStartTime = 0L
    private var groupEndTime = 0L
    val statistic: Statistic = Statistic()

    @MainThread
    fun runTasks() {
        subTasks.clear()
        collectSubTasks()
        executeTasks(subTasks.map { { executeSingleTaskWrapper(it) } })
    }

    protected abstract fun executeTasks(tasksWrapper: List<() -> Unit>)

    protected abstract fun collectSubTasks()

    private fun executeSingleTaskWrapper(task: SubTask, reschedule: Boolean = false) {
        try {
            if (!task.isStarted.compareAndSet(false, true)) {
                return
            }
            Log.d(
                TAG,
                "${this@TaskCollector.javaClass.simpleName} execute ${task.name} start...${if (reschedule) "(reschedule)" else ""}"
            )
            task.startTime = System.currentTimeMillis()
            task.innerTask.invoke()
            task.endTime = System.currentTimeMillis()
            task.duration = task.endTime - task.startTime
            Log.d(
                TAG,
                "${this@TaskCollector.javaClass.simpleName} execute ${task.name} cost: ${task.duration} ${if (reschedule) "(reschedule)" else ""}"
            )
        } catch (t: Throwable) {
            val msg = "Task running error:${this.javaClass.simpleName}:${task}"
            Log.e(TAG, msg)
            if (BuildConfig.DEBUG || DebugChannelHelper.isTestChannel()) {
                HandlerUtils.getMainHandler().post {
                    throw RuntimeException(msg, t)
                }
            } else {
//                AutoLog.ensureNotReachHere(t, msg)
            }
        }
    }

    protected fun addSubTask(name: String = "", subTask: () -> Unit) {
        subTasks.add(
            SubTask(
                subTask,
                name.ifEmpty { this.javaClass.simpleName + "*index_" + subTasks.size.toString() })
        )
    }

    protected fun markGroupStart() {
        groupStartTime = System.currentTimeMillis()
    }

    protected fun markGroupEnd() {
        groupEndTime = System.currentTimeMillis()
        Log.d(TAG, "====${this.javaClass.simpleName} total cost:${(groupEndTime - groupStartTime)}")
    }

    internal fun findOnePendingSubTask(): ((Boolean) -> Unit)? {
        val task = subTasks.find { t -> !t.isStarted.get() } ?: return null
        return {
            executeSingleTaskWrapper(task, it)
        }
    }


    inner class Statistic {

        fun getTasks(): List<SubTask> {
            return subTasks.toList()
        }

        fun getTasksFinishAfter(time: Long): List<SubTask> {
            return subTasks.filter { it.endTime > time }.toList()
        }

        fun getGroupStartTime(): Long {
            return groupStartTime
        }

        fun getGroupEndTime(): Long {
            return groupEndTime
        }

        fun getGroupDuration(): Long {
            return groupEndTime - groupStartTime
        }

    }
}